Skip to content

Add [TypeSelector] dropdown for [SerializeReference] and type repair#49

Open
VPDPersonal wants to merge 91 commits into
mainfrom
feature/serialize-reference-dropdown
Open

Add [TypeSelector] dropdown for [SerializeReference] and type repair#49
VPDPersonal wants to merge 91 commits into
mainfrom
feature/serialize-reference-dropdown

Conversation

@VPDPersonal

@VPDPersonal VPDPersonal commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Extends [TypeSelector] to [SerializeReference] and adds a full authoring + integrity toolkit for managed references. Every row works in both IMGUI and UIToolkit.

Area What it does
Picker on [SerializeReference] Hierarchical type dropdown on single / array / List<T> fields. Pick → instantiate, <None> → clear, the instance's nested props draw inline. Candidates default to the declared type, narrowed by [TypeSelector(typeof(IMelee))].
Open generics Offers Modifier<T>; arguments are inferred from a closed-generic field or picked in a follow-up window honouring constraints.
Editing Matching data preserved on type switch; Copy/Paste between compatible fields; multi-object edit applies one instance per object in a single Undo group.
Picker UX [TypeSelectorItem] (display name / Category/Name / tooltip / order / icon); per-project Favorites ★ + Recent; collapsible groups with in-header search.
Missing-type repair Reads the orphaned type straight from YAML (works around UUM-129100). Inline Fix re-points the ref and keeps its data — at any nesting depth, for saved assets, Prefab Mode and saved scenes. Smart Fix ranks the likeliest replacement for a one-click apply.
Aliased refs Make Unique action + deterministic per-rid colour stripes for shared instances.
Managed References window Tools → Aspid 🐍 → Managed References FastTools, four tabs: Home (overview + installable samples), Inspect Asset (whole graph from YAML — MISSING / SHARED badges, orphans, inline Fix, Open Source Prefab), Project Audit (project sweep grouped by broken type, Fix all with YAML diff preview, per-fix receipts with Undo, deep-links to Inspect) and Settings.
Editor theming Project-level theme override (AspidThemeSettings + Project Settings page) that re-skins every Aspid FastTools editor view; a reusable AspidWindowFooter (version + GitHub link) is pinned to the bottom of the window.
Project tooling Incremental usage index → Find Usages + sr: Quick Search; delete-script guard; proactive breakage notification; build/CI gate (SerializeReferenceCiGate.RunCheck); [SerializeReferenceRequired]; Project Settings page.
Authoring Drag a MonoScript onto a field; named templates; Link to Existing; picker-backed list +; Create New Script.
Sample / docs / tests SerializeReferences sample (weapons/effects, melee/ranged types, generic Modifier<T>, broken/shared demo prefabs) with a step-by-step tutorial (TypeSelectorTutorial scene + TUTORIAL.md EN/RU); README EN/RU + Documentation + CHANGELOG; first EditMode test assembly (Aspid.FastTools.Unity.Editor.Tests).

Notes for review

  • New code lives under Unity/Editor/Scripts/SerializeReferences/. The selector was extracted into a reusable view; TypeSelectorWindow is now a thin dropdown host (public Show API unchanged, gained optional filter / additionalTypes).
  • Trickiest spots: generic-argument resolution, YAML detect/rewrite (SerializeReferenceYamlEditor), at-any-depth repair through nested refs and [Serializable] containers, and the bulk rewrite (edits asset files directly — irreversible, hence the diff preview + confirmation).
  • Window merge: the old separate Repair Missing References and Managed References graph windows are now one workbench, and the Welcome view's overview is embedded as the Home tab (the main structural change since earlier revisions).
  • Shared editor UI: a new project-level theme override (AspidThemeSettings / AspidThemeSettingsProvider, applied across ~11 editor views) and a reusable AspidWindowFooter component pinned to the window bottom.
  • USS: migrated to BEM (aspid-fasttools-type-selector); stylesheet renamed to Aspid-FastTools-TypeSelector.uss (meta GUID preserved).
  • Also on this branch: the [TypeSelector] unification (Unify SerializeReference selector into [TypeSelector] with usage analyzer #50) and the Aspid.FastTools.Analyzers submodule — AFT0001 fix (Unity declares SerializeReference without the Attribute suffix), a widened AFT0003, plus new AFT0004 / AFT0005 diagnostics. The submodule pointer tracks the analyzer's feat/aft-usage-rules branch (to be merged into its main separately).

Linked issues

Closes #10

… drawer

Add a hierarchical type-selector dropdown for [SerializeReference] fields
(single, array and List<T>), reusing TypeSelectorWindow. Picking a concrete
type instantiates it, <None> clears the reference, the assigned instance's
nested properties are drawn inline under a foldout, and an unresolved stored
type is surfaced as a missing-type warning. Implemented for both IMGUI and
UIToolkit inspectors.

TypeSelectorWindow.Show gains an optional candidate-type filter (backward
compatible) used to exclude UnityEngine.Object, open generics, strings and
delegates.
Add a loadout-system sample exercising [SerializeReferenceSelector]: single
IWeapon field, List<IWeapon>, abstract StatusEffect base, and a nested
[SerializeReference] inside Railgun. Includes an IMGUILoadout + forcing editor
to demonstrate the IMGUI path, EN/RU README, and a package.json sample entry.
Add Loadout.prefab (UIToolkit) and IMGUILoadout.prefab (IMGUI) with pre-filled
managed references — single (Railgun + nested BurnEffect), List<IWeapon>
([Pistol, Shotgun]) and abstract-base (FreezeEffect / BurnEffect) — so the
sample can be inspected without building it by hand. Update the EN/RU sample
README "How to run" to drive the prefabs.
- Offer open generic definitions (e.g. Modifier<T>) as candidates: infer
  arguments from a closed-generic field, or resolve them in a new
  recursive, constraint-aware GenericArgumentSelectorWindow, validating
  the closed type against the field before assignment. Works in IMGUI and UIToolkit.
- Add an optional additionalTypes pass-through to TypeSelectorWindow /
  HierarchyBuilder / TypeInfo, and render generic names as Modifier<T>.
- Add a non-abstract Modifier<T> hierarchy (IModifier) with closed-generic
  subclasses, plus IModifier / Modifier<float> / List<IModifier> fields on
  Loadout and IMGUILoadout to exercise open-generic selection and the T picker.
- Document the generic flow in the sample README (EN/RU).
- Note open-generic support and the new additionalTypes parameter in the
  CHANGELOG, and add a generic bullet to the feature section of all four READMEs.
…he dropdown

- Resolve open generics inside TypeSelectorWindow as in-window argument pages (hierarchy, search, breadcrumb, live preview), removing the separate GenericArgumentSelectorWindow and its focus issues
- Extract generic resolution into GenericTypeResolver and add an argumentFilter parameter to TypeSelectorWindow.Show; the flow stays dormant unless open generics are present
- Format generic type names recursively so nested closed generics render fully (Modifier<Modifier<Int32>>)
- Resolve the generic type definition when locating a script so Open Script works for closed generics
@VPDPersonal VPDPersonal added type: feature New feature or capability status: work-in-progress Draft / not ready for review area: runtime Runtime / player code area: editor Editor-only code area: samples Sample projects labels Jun 7, 2026
VPDPersonal and others added 10 commits June 7, 2026 21:07
…eric argument selector

Open generic definitions reach the selector through the additionalTypes
path, which bypasses the name and CompilerGeneratedAttribute checks
applied to ordinary candidates. As a result anonymous types and closure
display classes (e.g. <>c__11<T>, <>f__AnonymousType0<...>) showed up as
argument candidates and bloated the unconstrained (object) list.

Exclude compiler-generated types in IsAssignableGenericDefinition so the
single gate for generic definitions filters them out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Carry the foldout caption on the toggle's aligned BaseField label so the
  type dropdown starts at the inspector value column, matching SerializableType
- Centre the header row so the open-script button lines up with the dropdown
- Split the dropdown into field/input nodes and cancel the EnumField caption's
  -2px left margin so the text indents like SerializableType
…/paste

- CreateInstancePreservingData carries matching [SerializeField] data across a
  type switch via JsonUtility ToJson/FromJsonOverwrite, instead of resetting
- SerializeReferenceClipboard backs a header right-click Copy/Paste menu scoped
  to the field (IMGUI ContextClick + UIToolkit ContextualMenuManipulator); paste
  builds an independent instance and is disabled when the type is not assignable
… references

- Add an Edit Type action to the missing-type warning: it opens an
  Assembly/Namespace/Class editor and rewrites the stored managed-reference
  type straight into the asset YAML (parser-free, no external deps), since
  Unity cannot reassign a missing reference through the serialization API.
  Applies to ScriptableObjects — the only assets where Unity preserves missing
  references (UUM-129100).
- Detect an aliased managed reference (two fields sharing one instance) and
  offer a Make Unique Reference action that clones it into an independent copy.
- Sample: add a WeaponPreset ScriptableObject with a pre-broken asset, plus
  shared-reference and missing-type demo prefabs, and document how to test both.
…YAML

- Replace the manual Edit-Type window with a Fix button that opens the existing
  hierarchical type picker; the chosen type is rewritten into the asset YAML.
- Read the missing type straight from the asset file (stored id + RefIds type)
  instead of Unity's serialization API, which reports nothing per-property and
  drops the reference on prefabs/GameObjects (UUM-129100). Repair now works on
  prefab assets too, not just ScriptableObjects.
- Detect strictly per-field by resolving the recorded type, removing the
  single-missing fallback that falsely flagged legitimately-empty fields.
- Scan a prefab/ScriptableObject's YAML for every orphaned managed reference —
  at any nesting depth and on any child object — by walking each document's flat
  RefIds block and flagging entries whose stored type no longer resolves.
- List each with its own Fix picker that rewrites the type in the file and
  reimports, reaching references the per-field drawer cannot (nested values,
  child-object components, anything Unity dropped to <None>) without Prefab Mode.
- Open via Tools → Aspid FastTools → Repair Missing References (auto-targets the
  current selection).
… Mode repair

Replace the bulky missing-type and shared-reference help boxes with a
compact yellow inline notice: a small warning icon, terse text and an
underlined, clickable action word (Fix / Make unique) with the full
detail on hover. Covers both the IMGUI and UIToolkit drawers.

Extend missing-type repair to objects open in Prefab Mode: detection
resolves the backing document through the prefab stage (matching the
asset's file id), and the fix is applied in memory — reassigning the
reference and recovering the orphaned field data — because rewriting the
open stage's file would be discarded on save. Saved assets keep the
YAML-rewrite path.

Reselect the inspected objects after a repair so Unity's cached
object-level missing-types banner clears, and guard the UIToolkit field
against the live SerializedObject being invalidated by a reimport.
…n containers

Generalise the YAML reference resolver behind the inline missing-type Fix to
walk a property path of any shape: through managed-reference chains and through
plain [Serializable] containers (struct/class fields and List<T> of them), so a
missing type buried in a slot or list element is detected and repaired inline
like a top-level field.

When an in-memory (Prefab Mode) repair replaces a missing reference that itself
carried missing nested references, clear those now-orphaned entries too so the
object's missing-types banner clears.

Add the SlottedLoadout sample demonstrating [SerializeReferenceSelector]
references inside a container field and a List<T> of containers.
- Redesign the Repair References window in the Welcome style: boxed
  asset card with an Aspid header, centred info/success hero states,
  warning-accented results header and amber gradient rows.
- Extract the hierarchical type selector from TypeSelectorWindow into a
  reusable TypeSelectorView; the window stays a thin dropdown host with
  an unchanged public Show API.
- Expand the selector inline as an accordion under the clicked Fix row
  (dark Aspid panel, one at a time, Escape collapses) instead of the
  floating grey dropdown.
- Migrate selector USS classes to BEM under the
  aspid-fasttools-type-selector block and rename the stylesheet to
  Aspid-FastTools-TypeSelector.uss; sync the asset field when Open()
  retargets an already-open window.
- The repair window moved to Tools → Aspid 🐍 → Repair Missing
  References FastTools; mirror the new path in the CHANGELOG, both
  root/Documentation README pairs and the sample READMEs.
@VPDPersonal VPDPersonal changed the title Add [SerializeReferenceSelector] dropdown for SerializeReference fields Add [SerializeReferenceSelector] dropdown and missing-type repair Jun 10, 2026
- Remove `[SerializeReferenceSelector]`; `[TypeSelector]` now also drives `[SerializeReference]` managed-reference fields, dispatching by property type in `TypeSelectorPropertyDrawer`.
- The attribute's base types narrow the candidate list below the declared field type — applied to both concrete types and open generic definitions, in the dropdown and the missing-type Fix picker.
- Update samples, CHANGELOG and READMEs (EN/RU) to the merged attribute.
- Add the `Aspid.FastTools.Analyzers` git submodule (VPDPersonal/Aspid.FastTools.Analyzers) carrying the `AFT*` [TypeSelector] usage rules.
- Ship the prebuilt `Aspid.FastTools.Analyzers.dll` with a `RoslynAnalyzer`-labelled meta (all platforms excluded), mirroring the generator DLL.
- Document the submodule and its manual rebuild/deploy in CLAUDE.md.
- Bump the analyzer submodule: AFT0003 now also flags a sealed class paired with an interface it does not implement; redeploy the prebuilt DLL.
- Switch the submodule URL from SSH to HTTPS so public clones resolve it without keys.
- Add the rebuild-analyzers-on-change PostToolUse hook script and the build-analyzer skill mirroring the generator pipeline; document both in CLAUDE.md.
…attribute

Unify SerializeReference selector into [TypeSelector] with usage analyzer
- Render each sample's package.json description as a wrapped line under a full-width divider in the samples list
- Lay out the sample name and its Import/Reimport action on a single header row
- Replace the imported-state "Select" (ping folder) with "Reimport", which re-imports over the existing copy after a confirm dialog
- Mirror the gradient button's hover recolor for the relocated name/action labels
- Introduce AspidWindowFooter: a transparent bottom bar pairing the package version (linking to its GitHub release tag) with a GitHub link, above a faded divider
- Pin it to the bottom of the Managed References window so it persists across every tab/mode instead of living inside the Welcome tab
- Drop the now-redundant inline footer from the Welcome UXML
- Wrap long Project Audit entry paths onto multiple lines (min-width:0) so the rid stays pinned right instead of overflowing into a horizontal scroll
- Stack one summary help-box per bulk Fix all and keep the results region when the last group clears, so prior receipts survive until an explicit Rescan instead of being overwritten or dropped
- Skip Unity scenes in the object-loading scanners via a shared IsScene helper (LoadAllAssetsAtPath cannot read .unity files); fall back to the YAML pass
- Defer the ListView add-button install one tick to avoid mutating the subtree mid-attach
- Restyle the Inspect Asset reference graph (GraphView + ReferenceGraph.uss)
- Each fix receipt carries a right-pinned Undo button that re-points its entries back to the original missing type, restoring the broken state (only the YAML type line moves, data blocks stay on disk, so the revert is faithful)
- Undo drops only its own receipt and re-renders the group list, leaving receipts and Undo buttons for other still-applied fixes standing
- Extract the scene/Prefab-Mode skip filter and the batched per-file rewrite into shared helpers used by both the fix and the undo
- EditorWindow.minSize only bounds the floating window; a docked column
  ignores it and squeezes the content into character-wrapped, vertical text.
- Give the mode container a min-width so the content slides off-screen
  (clipped) instead of collapsing; toolbar/footer stay flexible.
- Position the per-receipt Undo chip absolute in the help box's top-right
  corner instead of in the row's flex flow, so the title and message
  stretch edge to edge instead of being cut short at the chip.
- Picking <None> in the Project Audit Fix all picker now nulls the
  group's broken references instead of being a no-op.
- On disk: rewrite each field/array pointer to the null id (-2) and add
  Unity's RefIds null sentinel, dropping the broken payload
  (TryNullReference) — matches what Unity writes for a null reference.
- Open in Prefab Mode / a loaded scene: null the reference on the live
  object in memory (TryClearMissingReferenceInMemory), saved with the
  asset, since a file rewrite there would be clobbered on save.
- Stop rendering the -2 null sentinel as a graph node so cleared fields
  no longer surface as "rid -2" shared cards.
- Render unassigned [SerializeReference] fields (null sentinel rid -2) as
  dim <None> leaves so a cleared or never-set slot stays visible in the
  graph instead of dropping out.
- Carry the full field path of each nested reference (e.g.
  _primaryWeapon._chargeEffect) via a new ReferenceGraphEdge.
- Add scanner structural tests and an empty-fields YAML fixture; show the
  empty slots in the Loadout sample prefab. Refresh READMEs and CHANGELOG.
- The tab labels "Inspect Asset" / "Project Audit" read as generic tools;
  rename to "Asset References" / "Project References" so they reflect the
  managed-reference scope the window already declares.
- Sync the labels across code comments, CHANGELOG and the sample docs.
- Keep a document whose RefIds block resolves to zero real nodes but
  still holds a field pointer, so an asset whose every [SerializeReference]
  field is unassigned renders its slots as <None> instead of dropping out
  with a "No managed references" empty state.
- Only return null now when there is neither a node nor a field pointer
  (every managed-ref field is an empty list — nothing to graph).
- Add an all-unassigned fixture and a regression test.
- Picking <None> in a missing card's Fix picker now nulls the reference
  instead of doing nothing — an empty type name fell through the null-type
  guard and silently no-op'd.
- Add a Clear action on every assigned reference card that resets it to
  <None>, so a reference can be cleared from the graph everywhere, not just
  re-pointed when missing.
- Both route through a shared ClearReference (YAML null-sentinel rewrite);
  confirmed and not undoable, with a neutral-tinted __clear-reference button.
- Document the new clear behavior in the EN/RU READMEs.
- Every reference card in the Managed References graph is now an inline
  type dropdown: picking a type assigns or re-points the reference and
  <None> clears it. Drops the dedicated Clear button on assigned cards —
  resetting is done through the picker, not a separate action.
- Healthy and empty (unassigned) slots edit through the live
  SerializedObject API keyed by field path, so Unity creates or removes the
  RefIds entry as the Inspector would; missing references keep the YAML
  rewrite (Unity cannot reassign a missing type through the API).
- Persist live edits via SavePrefabAsset for prefab assets and
  SaveAssetIfDirty for ScriptableObjects, then rescan from disk.
- Neutral-tint the dropdown band; the amber accent moves to a --missing
  modifier so only a broken card reads as a call to action.
- Healthy and empty graph cards now label their dropdown band ("Change ▼" /
  "Assign ▼") instead of showing a lone chevron, matching the missing
  card's "Fix Missing ▼".
- The empty (<None>) card drops its dimmer fill/border override so it reads
  at the same weight as the live cards — it is now an editable dropdown, so
  only its dim italic <None> placeholder sets it apart.
Nesting is read from each card's field path (e.g. _primaryWeapon._chargeEffect),
so the per-depth left margin was redundant. Render every card flush in a flat
column; remove the depth parameter threaded through AppendNode / BuildNodeCard /
BuildEmptySlotCard / BuildBackEdgeCard and the IndentStep constant.
- The Asset References "Rescan" row sized to its field (height:auto, ~24px)
  while the Project References "Scan Project" button kept the gradient
  button's 30px default, so the two tabs' top panels differed in height. Pin
  the Rescan row to 30px so both action rows — and the panels — match.
- Fix two invalid unitless USS lengths in the same file: padding-left: 4 and
  margin-bottom: 2 (now 4px / 2px).
NestedLoadout.prefab is a three-level hierarchy (NestedLoadout → WeaponSlot
→ BackupSlot) with a Loadout on every object, each carrying a missing
[SerializeReference] type (GhostPistol / GhostBlade / GhostAura) the
Inspector can't reach outside Prefab Mode — plus a nested reference, an
empty <None> slot and healthy refs. Exercises the graph's per-component
multi-document view, missing types on child objects, and the inline
assign / re-point / clear dropdowns. Documented in the sample README EN/RU.
The per-document header bands floated transparent over the dotted canvas,
their fill showing only on hover. Give them the same resting card surface
as the node cards below (translucent dark fill + hairline border) so each
header reads at the same weight as its group; the amber issue hover glow is
kept.
- Bind Alt+1/2/3/0 to Home/Asset/Project/Settings via ShortcutManager with
  the window as context, so the keys work whenever the window is focused
  (a panel KeyDownEvent only fired while an inner element held focus)
- Show the live binding as a keyboard-cap badge on the two mode tabs and as
  a tooltip on every tab, reading it back from ShortcutManager so it tracks
  user rebinds in Edit > Shortcuts
- Point sample README EN/RU at the renamed Managed References FastTools window
- Add Sword to the listed IWeapon implementations in the sample README
- Fix the breakage notification log still naming the old repair menu
…References tab

A plain switch to the Project References tab no longer triggers a scan, even when the type-usage index is warm. The tab now always opens on the idle Scan panel and waits for a deliberate Scan Project click; only the breakage-notification deep-link still forces a scan. Drops the now-unused SerializeReferenceTypeUsageIndex.IsWarm gate.
…izeReferenceSettingsUI

Pull the rid-colour, auto-de-alias, build-gate and excluded-folder controls into a single SerializeReferenceSettingsUI.BuildControls, shared by the Project Settings provider and the in-window Settings tab so both render the same controls from one definition instead of duplicating the UI.
… tab

- ShowIdle now reuses ShowEmptyState to paint the info-toned snake icon
  with a headline and prompt, mirroring the Asset References tab's
  "No asset selected" hero instead of leaving a blank canvas.
- Split the "FastTools" suffix into a submenu level: Welcome / Asset
  References / Project References / Settings under Tools/Aspid 🐍/FastTools
- Add Project References and Settings menu entries (Project opens without
  auto-scan); priority gaps render the two separators between groups
- Point the breakage-notification log at the new Project References path
- Repoint every Tools/Aspid 🐍 menu reference to the new nested paths
  (FastTools → Welcome / Asset References / Project References / Settings)
- Reframe the root + Documentation README bullets that still described two
  separate windows as the Asset References / Project References tabs
- Drop the now-defunct standalone-window menu paths from CHANGELOG and
  refresh the merge entry; swap the sample/tutorial paths to match
- Apply a consistent Field → Try it → Notice layout to lessons 3, 5, 7, 8
- Split the maintenance section into subsections with an Asset/Project References tab table
- Note the in-window Settings tab alongside Project Settings
- Fix the required-reference cross-link to point at the build/CI gate section
- Mirror every change across the EN and RU tutorials
- Replace the unreleased [SerializeReferenceRequired] attribute with Required/RequiredMessage on [TypeSelector]; drop the never-read AllowMissingType.
- Extend required to [TypeSelector] string fields: an empty value is flagged in the inspector (IMGUI + UIToolkit) and by the build/CI gate, on top of the existing managed-reference path.
- Render the required notice as a warning instead of info, sharing one IMGUI notice helper across the managed-reference and string paths.
- Migrate the sample, EN/RU tutorial and CHANGELOG to the new API.
- Generalize the left-edge rid stripe into a status stripe: a missing type now paints it warning amber, while shared references keep their per-rid colour (shared takes priority)
- Remove the redundant rid colour chip from the shared-reference notice; the left stripe already conveys the colour
- Inset the notice so its warning icon clears the 3 px stripe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: editor Editor-only code area: runtime Runtime / player code area: samples Sample projects status: needs-review Ready for review type: feature New feature or capability

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

SerializeReferenceSelector

1 participant